home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
Source
/
Unfolder 2.0 Source
< prev
Wrap
Text File
|
1994-01-10
|
31KB
|
1,297 lines
Date: Wed, 20 Oct 1993 22:53:52 -0400 (EDT)
From: hshubs@BIX.com
Subject: Unfolder 2.0 source
This is the source code for Unfolder 2.0, so it might be named unfolder20.c,
and put in the same directory as unfolder20.hqx.
/*
* The Unfolder - program to reconstitute MacBinary (II) encoded files.
*
* by various BIXen in the 'mac.hack/tutorial', started on 25 December 1989
*
* Originally written by Don Sample, Howard Shubs, and Bob Perkins on
* the BYTE Information eXchange (BIX), this program is intended to allow
* people with a C compiler and no way to transfer resources to their Mac
* to create a way to deal with Mac files which have been downloaded to
* non-Macintosh hardware. This program will, when told to Unfold,
* reconstitute a MacBinary version 1 or 2 file to its original state.
*
* The program will work as-is under THINK C 5.0.2, with just the MacTraps
* and ANSI-small libraries in the project file with it. If you are using
* other environments, you may need to modify the file. If possible,
* please use conditional compilation and send a diffs file to Howard Shubs
* at hshubs@bix.com.
*
*
* #includes
*/
/*#include "Headers"*/
#include <stdio.h>
#include <stdarg.h>
#include <Traps.h>
#include <Errors.h>
#include <Types.h>
#include <GestaltEqu.h>
#include <OSUtils.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <ToolUtils.h>
#include <Fonts.h>
#include <OSEvents.h>
#include <Menus.h>
#include <Events.h>
#include <Windows.h>
#include <Files.h>
#include <Resources.h>
#include <Dialogs.h>
#include <StandardFile.h>
#include <Desk.h>
#include <Finder.h>
#include <Pascal.h>
#include <TextEdit.h>
/*
* #defines
*/
#define ALL_TYPES -1L
#define BASE_RES_ID 400
#define NIL_POINTER 0L
#define MOVE_TO_FRONT -1L
#define REMOVE_ALL_EVENTS 0
#define MIN_SLEEP 0
#define NIL_MOUSE_REGION 0L
#define SUSPEND_RESUME_BIT 0x0001
#define RESUMING 1
#define APPLE_MENU_ID 128
#define FILE_MENU_ID 129
#define EDIT_MENU_ID 130
#define ABOUT_ITEM 1
#define UNFOLD_ITEM 1
#define QUIT_ITEM 2
#define MACBINARY_VERSION 129
#define SCROLL_WIDTH 15
#define NIL_STRING "\p"
#define NUM_MASTER_BLOCKS 1 /* this number is semi-arbitrary */
#define BF_OWN_APPL 0x0002 /* Some finder info flags not defined */
#define F_INITED 0x0100 /* in FileMgr.h */
#define F_CHANGED 0x0200
#define F_BUSY 0x0400
/*
* MacBinary II Header #defines
*/
#define OLD_V_N 0
#define LEN_F_N 1
#define FILENAME 2
#define FILE_TYPE 65
#define FILE_CREA 69
#define ORIG_F_FLAGS 73
#define ZERO_FILL_I 74
#define FILE_V_POS 75
#define FILE_H_POS 77
#define WIN_FOL_ID 79
#define PROTECTED 81
#define ZERO_FILL_II 82
#define LEN_D_FORK 83
#define LEN_R_FORK 87
#define F_CREA_DATE 91
#define F_MOD_DATE 95
#define LEN_GET_INFO 99
#define FINDER_FLAGS 101
#define LEN_TOT_F 116
#define LEN_SEC_H 120
#define MBII_VERS_UP 122
#define MBII_VERS_READ 123
#define CRC 124
/*
* Structures
*/
typedef struct Header /* MacBinary header data */
{
unsigned char oldVN; /* old version number */
char fName[64]; /* file name */
FInfo finderInfo; /* Original Finder information record */
unsigned char protected;
long lenDF; /* length of data fork */
long lenRF; /* length of resource fork */
long fCreaDate; /* file creation date */
long fModDate; /* file's last modified date */
int lenGetInfo; /* length of Get Info comment */
unsigned char finderFlags; /* Low byte of finder flags */
long lenTotF; /* length of total files for unpacking */
int lenSecH; /* length of any secondary header */
unsigned char mbIIVersUp; /* Version of MB II used for upload */
unsigned char mbIIVersRead; /* Version of MB II needed to read file */
int crc;
} Header;
typedef struct DialogItem /* Dialog item */
{
long pointer;
Rect boundsRect;
unsigned char itemType;
unsigned char itemData[3];
} DialogItem;
typedef struct DialogList /* DITL record */
{
short itemCount;
DialogItem items[2];
} DialogList;
/*
* Function Prototypes:
*/
void AboutAppl (void);
/*short int CalcCRC(unsigned char *dataBuf, short int size);*/
short int CalcCRC(register unsigned char *dataBuf, register int size);
OSErr CreateFile(Str255 fName, int vRefNum, OSType creator,
OSType type);
Rect DeviceRect (void);
int GetAnEvent(int eventMask, EventRecord *event, long sleep,
RgnHandle mouseRgn);
void HandleAppleChoice (short theItem);
void HandleFileChoice (short theItem);
void HandleEditChoice (short theItem);
void HandleEvent (EventRecord *theEvent);
void HandleMenuChoice (long menuChoice);
void HandleMouseDown (EventRecord *thisEvent);
unsigned char HeaderCheck (short int, Header *);
void Init (void);
void MessageDialog(char *format, ...);
void Pause(void);
void ProcessFile (void);
void ToolBoxInit(void);
int TrapAvailable(unsigned trapNumber, int trapType);
void Unfold (short int refNum, Header *thisHeader,
SFReply *outputFile);
int WNEIsImplemented(void);
/*
* Globals
*
*/
EventRecord gTheEvent;
MenuHandle gAppleMenu;
MenuHandle gFileMenu;
MenuHandle gEditMenu;
Point gWhere;
Boolean gDone;
Boolean gWNEExists;
short gQDVersion;
/*
* FUNCTION:
* ToolBoxInit
*
* PURPOSE:
* Initialize the ToolBox Managers
*/
void ToolBoxInit()
{
InitGraf (&(qd.thePort));
InitFonts ();
FlushEvents (everyEvent, REMOVE_ALL_EVENTS);
InitWindows ();
InitMenus ();
TEInit ();
InitDialogs (NIL_POINTER);
InitCursor ();
}
/*
* FUNCTION:
* Pause
*
* PURPOSE:
* Wait for the user to click the mouse, or to press the return
* or Enter key.
*/
void Pause()
{
int button;
EventRecord myEvent;
button = false;
while (!button)
{
if (GetAnEvent(everyEvent, &myEvent, 0L, NIL_POINTER))
{
if (myEvent.what == mouseDown)
button = true;
else if (myEvent.what == keyDown)
{
if (((myEvent.message & charCodeMask) == 0x0d) ||
((myEvent.message & charCodeMask) == 0x03))
button = true;
}
}
}
while (StillDown())
;
}
/*
* FUNCTION:
* MessageDialog
*
* PURPOSE:
* Display an error message and then pause until the user clicks the
* mouse button
*
* INPUT:
* char *format - A NULL terminated format string, using
* the same rules as printf.
* ... - Other parameters as specified in the
* format string
*/
void MessageDialog(char *format, ...)
{
char messageStr[256];
va_list data;
OSErr thisErr;
va_start(data, format);
vsprintf(messageStr, format, data);
CtoPstr(messageStr);
ParamText ((ConstStr255Param) messageStr, NIL_STRING, NIL_STRING, NIL_STRING);
thisErr = CautionAlert (BASE_RES_ID, NIL_POINTER);
va_end(data);
}
#define _Unimplemented 0xA89F
#define _WaitNextEvent 0xA860
/*
* FUNCTION:
* GetAnEvent
*
* PURPOSE:
* Get an event from the input queue using whichever is appropriate:
* GetNextEvent, or WaitNextEvent
*
* INPUTS:
* int eventMask - Types of events to get
* EventRecord *event - Where to put retreived event
* long sleep - amount of time to surrender to
* background tasks
* RgnHandle mouseRgn - Global region containing mouse.
*
* RETURNS:
* int TRUE if an event was retrieved.
*/
enum /* Which routine to call: Get... or WaitNextEvent */
{
notKnown,
getNext,
waitNext
};
int GetAnEvent(int eventMask, EventRecord *event, long sleep,
RgnHandle mouseRgn)
{
int result;
static int callRoutine = notKnown;
if (callRoutine == notKnown)
{
if (WNEIsImplemented())
callRoutine = waitNext;
else
callRoutine = getNext;
}
if (callRoutine == waitNext)
result = WaitNextEvent(eventMask, event, sleep, mouseRgn);
else
{
SystemTask();
result = GetNextEvent(eventMask, event);
}
return(result);
}
/*
* FUNCTION:
* TrapAvailable
*
* PURPOSE:
* Determine if a specific trap routine is available
*
* INPUTS:
* unsigned tNumber - trap number
* int tType - trap type (OS or Toolbox)
*
* RETURNS:
* int TRUE if trap is available
* false otherwise
*/
TrapAvailable(unsigned tNumber, int tType)
{
/* Check and see if the trap exists. */
return(NGetTrapAddress(tNumber, tType) !=
GetTrapAddress(_Unimplemented));
}
/*
* FUNCTION:
* WNEIsImplemented
*
* PURPOSE:
* Determine if the WaitNextEvent routine is available
*
* RETURNS:
* int TRUE if WNE is available
* FALSE otherwise
*/
WNEIsImplemented()
{
SysEnvRec theWorld; /* used to check if machine has new traps */
/*
* Since WaitNextEvent and HFSDispatch both have the same trap
* number ($60), we can only call TrapAvailable for WaitNextEvent
* if we are on a machine that supports separate OS and Toolbox
* trap tables. We call SysEnvirons and check if machineType < 0.
*/
SysEnvirons(1, &theWorld);
/* Even if we got an error from SysEnvirons, the SysEnvirons glue
has set up machineType. */
if (theWorld.machineType < 0)
{
/* this ROM doesn't have separate trap tables or WaitNextEvent */
return(false);
}
else
{
/* check for WaitNextEvent */
return(TrapAvailable(_WaitNextEvent, ToolTrap));
}
}
/*
* FUNCTION:
* CreateFile
*
* PURPOSE:
* Create a file, deleting any previous version if necessary.
*
* INPUTS:
* Str255 fName - File name
* int vRefNum - Directory to create file in
* OSType creator - File's creator ID
* OSType type - File type
*
* RETURNS:
* OSErr - noErr if successful.
*/
OSErr CreateFile(Str255 fName, int vRefNum, OSType creator, OSType type)
{
OSErr thisErr;
thisErr = Create (fName, vRefNum, creator, type);
if (thisErr == dupFNErr)
{
thisErr = FSDelete (fName, vRefNum);
if (thisErr == noErr)
thisErr = Create (fName, vRefNum, creator, type);
}
return (thisErr);
}
/*
* FUNCTION:
* CalcCRC
*
* PURPOSE:
* Calculate a CCITT CRC for a data buffer
*
* INPUTS:
* unsigned char *data - pointer to data buffer to perform CRC on
* int size - size of data buffer
*
* RETURNS:
* int - CRC for data buffer. If data buffer contains as its
* last 2 bytes the CRC for the previous bytes the return
* value is 0
*/
#define CCITT_CRC_GEN 0x1021
short int CalcCRC(register unsigned char *dataBuf, register int size)
{
register unsigned short crc = 0;
register unsigned short dataByte;
register int i;
while (size--)
{
dataByte = *dataBuf++ << 8;
for (i = 8; i > 0; i--)
{
if ((dataByte ^ crc) & 0x8000)
crc = (crc << 1) ^ CCITT_CRC_GEN;
else
crc <<= 1 ;
dataByte <<= 1;
}
}
return(crc);
}
/*
* FUNCTION:
* AboutAppl
*
* PURPOSE:
* Tell about the program
*
*/
void AboutAppl (void)
{
Rect bounds = {58, 108, 192, 400};
WindowPtr thisWindow;
GrafPtr oldPort;
GetPort (&oldPort);
thisWindow = NewWindow (NIL_POINTER, &bounds, "\p", true, 1, (WindowPtr) -1L,
false, 0L);
bounds = thisWindow->portRect;
SetPort (thisWindow);
/* 123456789 0 123 4 567890123456789 0123456789 */
TextBox ("Unfolder\015\015by\015\015Howard S Shubs\015Don Sample\015Bob Perkins", 51L, &bounds, 1);
Pause ();
SetPort (oldPort);
DisposeWindow (thisWindow);
}
/*
* FUNCTION:
* DeviceRect
*
* PURPOSE:
* Get the bounds rect of the main screen.
*
* RETURNS:
* Rect the bounds rect of the main screen.
*
*/
Rect DeviceRect (void)
{
GDHandle device;
Rect devRect;
if (gQDVersion > 0)
{
device = GetMainDevice ();
devRect = (**(device)).gdRect;
}
else
devRect = qd.screenBits.bounds;
return devRect;
}
/*
* FUNCTION:
* HandleAppleChoice
*
* PURPOSE:
* Deal with selection from the Apple menu
*
* INPUTS:
* short int theItem: contains the selection from the menu
*
*/
void HandleAppleChoice (short theItem)
{
Str255 accName;
int accNumber;
short int itemNumber;
DialogPtr AboutDialog;
switch (theItem)
{
case ABOUT_ITEM:
AboutAppl ();
break;
default:
GetItem (gAppleMenu, theItem, accName);
accNumber = OpenDeskAcc (accName);
break;
}
}
/*
* FUNCTION:
* HandleEditChoice
*
* PURPOSE:
* Deal with selection from the Edit menu by passing them to the
* system.
*
* INPUTS:
* short int theItem: contains the selection from the menu
*
*/
void HandleEditChoice (short theItem)
{
if (!SystemEdit (theItem - 1))
;
}
/*
* FUNCTION:
* HandleFileChoice
*
* PURPOSE:
* Deal with selection from the File menu
*
* INPUTS:
* short int theItem: contains the selection from the menu
*
*/
/*{}{}{}*/
void HandleEvent (EventRecord *thisEvent)
{
char theChar;
if (gWNEExists)
WaitNextEvent (everyEvent, thisEvent, MIN_SLEEP, NIL_MOUSE_REGION);
else
{
SystemTask ();
GetNextEvent (everyEvent, thisEvent);
}
switch (thisEvent->what)
{
case nullEvent:
break;
case mouseDown:
HandleMouseDown (thisEvent);
break;
case keyDown:
case autoKey:
theChar = (thisEvent->message) & charCodeMask;
if (( (thisEvent->modifiers) & cmdKey) != 0)
HandleMenuChoice (MenuKey(theChar));
break;
case updateEvt:
/* BeginUpdate ((WindowPtr) (thisEvent->message) );
UpdateWindow ((WindowPtr) (thisEvent->message));
EndUpdate ((WindowPtr) (thisEvent->message) );*/
break;
case app4Evt:
if ( ( (thisEvent->message) & SUSPEND_RESUME_BIT) == RESUMING)
{
/* gState.applFlags.isSuspended =
((thisEvent->message) & 0x01) == 0;*/
}
break;
}
}
/*
* FUNCTION:
* HandleFileChoice
*
* PURPOSE:
* Deal with selection from the File menu
*
* INPUTS:
* short int theItem: contains the selection from the menu
*
*/
void HandleFileChoice (short theItem)
{
switch (theItem)
{
case UNFOLD_ITEM:
ProcessFile ();
break;
case QUIT_ITEM:
gDone = true;
break;
}
}
/*
* FUNCTION:
* HandleMenuChoice
*
* PURPOSE:
* Deal with selection of menu items
*
* INPUTS:
* long int menuChoice: contains the selection from a menu
*
* RETURNS:
* unsigned char - TRUE if the file is MacBinary
*
*/
void HandleMenuChoice (long menuChoice)
{
short theMenu;
short theItem;
if (menuChoice != 0)
{
theMenu = HiWord (menuChoice);
theItem = LoWord (menuChoice);
switch (theMenu)
{
case APPLE_MENU_ID:
HandleAppleChoice (theItem);
break;
case FILE_MENU_ID:
HandleFileChoice (theItem);
break;
case EDIT_MENU_ID:
HandleEditChoice (theItem);
break;
}
HiliteMenu (0);
}
}
/*
* FUNCTION:
* HandleMouseDown
*
* PURPOSE:
* Deal with mouseDown events.
*
* INPUTS:
* EventRecord *thisEvent points to the current event record.
*
*/
void HandleMouseDown (EventRecord *thisEvent)
{
Rect devRect;
long menuChoice;
short int thePart;
WindowPeek window;
long windSize;
thePart = FindWindow ((thisEvent->where), (WindowPtr *) &window);
switch (thePart)
{
case inMenuBar:
menuChoice = MenuSelect ((thisEvent->where));
HandleMenuChoice (menuChoice);
break;
case inSysWindow:
SystemClick (thisEvent, (WindowPtr) window);
break;
case inDrag:
devRect = DeviceRect ();
DragWindow ((WindowPtr) window, (thisEvent->where), &devRect);
break;
case inGrow:
devRect = DeviceRect ();
windSize = GrowWindow ((WindowPtr) window, (thisEvent->where),
&devRect);
if (windSize > 0)
{
Rect bad;
Point p;
/* invalidate the part of the window with the grow icon */
bad = (window->port).portRect;
bad.top = bad.bottom - SCROLL_WIDTH;
bad.left = bad.right - SCROLL_WIDTH;
InvalRect (&bad);
}
SizeWindow ((WindowPtr) window, LoWord (windSize),
HiWord (windSize), true);
break;
case inGoAway:
/*if (TrackGoAway ((WindowPtr) window, (thisEvent->where) ) )
DoCloseWindow ((WindowPtr) window);*/
break;
}
}
/*
* FUNCTION:
* HeaderCheck
*
* PURPOSE:
* Look at a selected file to determine if it is a MacBinary (II)
* file or not.
*
* INPUTS:
* short int refNum - reference number of the opened file to be
* inspected.
* Header *thisHeader - MacBinary header record to return data in.
*
* RETURNS:
* unsigned char - TRUE if the file is MacBinary
*
*/
unsigned char HeaderCheck (short int refNum, Header *thisHeader)
{
long int count = 128;
OSErr thisErr;
short int i;
unsigned char *buffer;
unsigned char soFarSoGood = false;
/* Go to the beginning of the file pointed to by refNum and
read the first (count) bytes. */
thisErr = SetFPos (refNum, fsFromStart, 0L);
if (thisErr == noErr)
{
buffer = (unsigned char *) NewPtr (count);
if ((thisErr = MemError()) == noErr)
{
thisErr = FSRead (refNum, &count, buffer);
if (thisErr == noErr)
{
/* Transfer the data read from the file into thisHeader */
/* First, check to make sure that the mbz bytes ARE zero */
if (buffer[OLD_V_N] == 0 && buffer[ZERO_FILL_I] == 0)
soFarSoGood = true;
/* Transfer the file name, and make sure its length is > 0 */
if (buffer[LEN_F_N] > 0 && buffer[LEN_F_N] < 64 && soFarSoGood)
{
BlockMove (&buffer[LEN_F_N], &(thisHeader->fName),
buffer[LEN_F_N]+1);
/* Get the finderInfo */
BlockMove (&buffer[FILE_TYPE], &(thisHeader->finderInfo),
sizeof(FInfo));
/* Get the "protected" flag */
thisHeader->protected = buffer[PROTECTED];
/* Get the fork sizes */
BlockMove (&buffer[LEN_D_FORK], &(thisHeader->lenDF),
8);
/* Get file's dates */
BlockMove (&buffer[F_CREA_DATE], &(thisHeader->fCreaDate),
8);
/* Get length of "Get Info" comment */
BlockMove (&buffer[LEN_GET_INFO], &(thisHeader->lenGetInfo),
2);
/* Low Finder flags */
thisHeader->finderFlags = buffer[FINDER_FLAGS];
/* Total size of files */
BlockMove (&buffer[LEN_TOT_F], &(thisHeader->lenTotF),
4);
/* Get length of secondary header */
BlockMove (&buffer[LEN_SEC_H], &(thisHeader->lenSecH),
2);
/* Get the versions of MacBinary II */
thisHeader->mbIIVersUp = buffer[MBII_VERS_UP];
thisHeader->mbIIVersRead = buffer[MBII_VERS_READ];
/* Get the CRC of the header */
BlockMove (&buffer[CRC], &(thisHeader->crc), 2);
soFarSoGood = (CalcCRC(buffer, 124)
== thisHeader->crc)
&& (buffer[MBII_VERS_READ] == MACBINARY_VERSION);
if (!soFarSoGood && (buffer[ZERO_FILL_II] == 0))
{
/* Make more comparisons to make sure that this
is really a MacBinary I file. */
for (i = 101; buffer[i]==0 && i < 126; i++);
/* If "i" is >= 126, the loop above completed
without finding a non-zero, so this is MacBinary I. */
if (soFarSoGood = i >= 126)
{
/* It is, it really _is_ MacBinary I */
/* Low Finder flags */
thisHeader->finderFlags = 0;
/* Total size of files */
thisHeader->lenTotF = 0;
/* Get length of secondary header */
thisHeader->lenSecH = 0;
/* Get the versions of MacBinary II */
thisHeader->mbIIVersUp = MACBINARY_VERSION;
thisHeader->mbIIVersRead = MACBINARY_VERSION;
/* Get the CRC of the header */
thisHeader->crc = 0;
}
}
}
else
soFarSoGood = false;
};
DisposPtr ((Ptr) buffer);
thisErr = MemError ();
}
}
if (thisErr != noErr)
MessageDialog("Error: %d reading MacBinary header", thisErr);
else if (!soFarSoGood)
MessageDialog("This is not a MacBinary file");
return soFarSoGood;
}
/*
* FUNCTION:
* Unfold
*
* PURPOSE:
* Restore the forks of a MacBinary encoded file.
*
* INPUTS:
* short int refNum - reference number of the opened file to be
* used as input.
* Header *thisHeader - MacBinary header information
* SFReply *thisFile - Information needed to access the created
* file.
*
* RETURNS:
* unsigned char - TRUE if the file is MacBinary
* Header *thisHeader - MacBinary header record to return data in.
*
*/
void Unfold (short int refNum, Header *thisHeader, SFReply *thisFile)
{
char *buffer;
char bufferAllocated;
long int buffSize;
long int forkSize;
long int offSet;
/* long int grow;*/
short int outRefNum; /* reference number for the output file */
OSErr thisErr; /* status code */
/* Allocate memory for a buffer by this rule:
Never allocate less than MIN_BUFFER, or more than MAX_BUFFER.
Between those two, allocate what's available less LEAVE_FREE,
as long as that isn't less than MIN_BUFFER. */
#define MIN_BUFFER 4*1024L
#define MAX_BUFFER 256*1024L
#define LEAVE_FREE 32*1024L
buffSize = MaxBlock (); /*MaxMem (&grow);*/
buffSize = buffSize > MAX_BUFFER + LEAVE_FREE ?
MAX_BUFFER :
buffSize < MIN_BUFFER ?
MIN_BUFFER :
buffSize - LEAVE_FREE < MIN_BUFFER ?
MIN_BUFFER :
buffSize - LEAVE_FREE;
buffer = NewPtr (buffSize);
bufferAllocated = (thisErr = MemError()) == noErr;
if (!bufferAllocated)
MessageDialog ("Couldn't allocate a work buffer. Give this program more memory, then run it again.");
/* Is there a data fork? If there is, rebuild it. */
if ((thisHeader->lenDF > 0) && bufferAllocated)
{
Ptr source;
Size count;
Str255 dest;
source = (Ptr) &((*thisFile).fName);
count = (char) *source;
count = BitAnd (count, 0x000000ffL) + 1L;
BlockMove (source, dest, count); /* {}{}{} */
forkSize = thisHeader->lenDF;
if ((thisErr = FSOpen (dest, thisFile->vRefNum, &outRefNum))
== noErr)
{
/* position beyond the MacBinary header */
if ((thisErr = SetFPos (refNum, fsFromStart, 128)) == noErr)
{
while (thisErr == noErr && forkSize > buffSize)
{
thisErr = FSRead (refNum, &buffSize, buffer);
if (thisErr == noErr)
thisErr = FSWrite (outRefNum, &buffSize, buffer);
forkSize -= buffSize;
}
if (thisErr == noErr)
{
thisErr = FSRead(refNum, &forkSize, buffer);
if (thisErr == noErr)
thisErr = FSWrite (outRefNum, &forkSize, buffer);
}
}
if (thisErr == noErr)
{
thisErr = FSClose (outRefNum);
if (thisErr == noErr)
thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
else
FlushVol (NIL_POINTER, thisFile->vRefNum);
}
else
{
FSClose (outRefNum);
FlushVol (NIL_POINTER, thisFile->vRefNum);
}
}
}
/* Is there a resource fork? If there is, rebuild it. */
if ((thisHeader->lenRF > 0) && bufferAllocated)
{
Ptr source;
Size count;
Str255 dest;
source = (Ptr) &((*thisFile).fName);
count = (char) *source;
count = BitAnd (count, 0x000000ffL) + 1L;
BlockMove (source, dest, count); /* {}{}{} */
forkSize = thisHeader->lenRF;
if ((thisErr = OpenRF (dest, thisFile->vRefNum, &outRefNum))
== noErr)
{
/* position beyond the MacBinary header and the Data Fork */
offSet = (128 - (thisHeader->lenDF % 128)) & 0x7f;
if ((thisErr = SetFPos (refNum, fsFromStart,
(thisHeader->lenDF) + offSet + 128)) == noErr)
{
while (thisErr == noErr && forkSize > buffSize)
{
thisErr = FSRead (refNum, &buffSize, buffer);
if (thisErr == noErr)
thisErr = FSWrite (outRefNum, &buffSize, buffer);
forkSize -= buffSize;
}
if (thisErr == noErr)
{
thisErr = FSRead(refNum, &forkSize, buffer);
if (thisErr == noErr)
thisErr = FSWrite (outRefNum, &forkSize, buffer);
}
}
if (thisErr == noErr)
{
thisErr = FSClose (outRefNum);
if (thisErr == noErr)
thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
else
FlushVol (NIL_POINTER, thisFile->vRefNum);
}
else
{
FSClose (outRefNum);
FlushVol (NIL_POINTER, thisFile->vRefNum);
}
}
}
if (bufferAllocated)
DisposPtr (buffer);
if (thisErr != noErr)
MessageDialog("Error: %d unfolding MacBinary file", thisErr);
}
/*
* FUNCTION:
* ProcessFile
*
* PURPOSE:
* Determine our input and output files, then do the processing.
*
*/
void ProcessFile (void)
{
Boolean running;
int i;
SFTypeList typeList;
SFReply inputFile, outputFile;
Header mbHeader;
OSErr thisErr;
short int refNum;
ParamBlockRec paramBlock;
unsigned int finderFlags;
SFGetFile (gWhere, NIL_POINTER, NIL_POINTER, ALL_TYPES, typeList,
NIL_POINTER, &inputFile);
running = inputFile.good == true;
if (running)
{
if ((thisErr = FSOpen (inputFile.fName, inputFile.vRefNum,
&refNum)) != noErr)
{
PtoCstr((unsigned char *) inputFile.fName);
MessageDialog("Error: %d opening file %s", thisErr, inputFile.fName);
}
/* read the MacBinary header. If it's okay, decode the file. */
else if (HeaderCheck (refNum, &mbHeader))
{
Str255 *string = (Str255 *) &(mbHeader.fName);
SFPutFile (gWhere, NIL_POINTER, *string,
NIL_POINTER, &outputFile);
if (outputFile.good)
{
if ((thisErr = CreateFile(outputFile.fName,
outputFile.vRefNum, mbHeader.finderInfo.fdCreator,
mbHeader.finderInfo.fdType)) != noErr)
{
PtoCstr((unsigned char *) outputFile.fName);
MessageDialog("Error: %d creating file %s", thisErr,
outputFile.fName);
running = false;
}
else
{
/* copy data & resource forks from input file */
Unfold (refNum, &mbHeader, &outputFile);
paramBlock.fileParam.ioCompletion = NIL_POINTER;
paramBlock.fileParam.ioNamePtr = (StringPtr) &outputFile.fName;
paramBlock.fileParam.ioVRefNum = outputFile.vRefNum;
paramBlock.fileParam.ioFVersNum = 0;
/* Set the finder Info for the file */
paramBlock.fileParam.ioFlFndrInfo = mbHeader.finderInfo;
finderFlags = mbHeader.finderInfo.fdFlags;
finderFlags |= mbHeader.finderFlags;
finderFlags &= ~(fOnDesk | BF_OWN_APPL | F_INITED |
F_CHANGED | F_BUSY);
paramBlock.fileParam.ioFlFndrInfo.fdFlags = finderFlags;
paramBlock.fileParam.ioFlFndrInfo.fdFldr = 0;
paramBlock.fileParam.ioFlFndrInfo.fdLocation.h = 0;
paramBlock.fileParam.ioFlFndrInfo.fdLocation.v = 0;
/* Set the Creation and Modification dates on the file */
paramBlock.fileParam.ioFlCrDat = mbHeader.fCreaDate;
paramBlock.fileParam.ioFlMdDat = mbHeader.fModDate;
thisErr = PBSetFInfo (¶mBlock, false);
if (thisErr != noErr)
MessageDialog("Error: %d, setting file attributes", thisErr);
}
}
}
thisErr = FSClose (refNum);
}
}
/*
* FUNCTION:
* Init
*
* PURPOSE:
* Initialize the environment, establish menus, and set variables.
*
* SIDE EFFECTS:
* Initializes the environment, establishes menus, and initializes
* the global variables.
*
*/
void Init (void)
{
long response;
Boolean gestaltExists;
Boolean sysEnvironsExists;
SysEnvRec *theWorld;
OSErr thisErr;
Handle thisHandle;
DialogList errorDITL;
AlertTemplate thisAlert;
/* Initialize everything */
/* Init the toolbox routines */
ToolBoxInit();
/* Make menus */
gAppleMenu = NewMenu (APPLE_MENU_ID, "\p\024");
gFileMenu = NewMenu (FILE_MENU_ID, "\pFile");
gEditMenu = NewMenu (EDIT_MENU_ID, "\pEdit");
InsertMenu (gAppleMenu, 0);
InsertMenu (gFileMenu, 0);
InsertMenu (gEditMenu, 0);
/* Add items to menus */
AppendMenu (gAppleMenu, "\pAbout Unfolder...;(-");
AddResMenu (gAppleMenu, 'DRVR');
AppendMenu (gFileMenu, "\pUnfold.../U;Quit/Q");
AppendMenu (gEditMenu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/X;Clear");
/* Draw the menu bar */
DrawMenuBar ();
/* Specify the point at which the SF dialogs will appear */
gWhere.v = 100;
gWhere.h = 100;
/* We're not done yet */
gDone = false;
/* Can we call WaitNextEvent? */
gWNEExists = WNEIsImplemented();
/* What kind of Quickdraw do we have? */
gestaltExists = TrapAvailable (_GestaltDispatch, ToolTrap);
sysEnvironsExists = TrapAvailable (_SysEnvirons, OSTrap);
if (gestaltExists)
thisErr = Gestalt ('qd ', &response);
else
{
if (sysEnvironsExists)
{
theWorld = (SysEnvRec *) NewPtr (sizeof (SysEnvRec));
if (theWorld == NIL_POINTER)
{
thisErr = MemError ();
MessageDialog("Error: %d allocating memory", thisErr);
}
thisErr = SysEnvirons (1, theWorld);
response = (long) theWorld->hasColorQD;
DisposPtr ((Ptr) theWorld);
}
else
response = 0L;
}
gQDVersion = LoWord (response) >> 8;
/* Do the miscellaneous resources exist yet? Check for one. If
that doesn't exist, assume that none of them do and create them. */
thisHandle = GetResource ('ALRT', BASE_RES_ID);
if (thisHandle == NIL_POINTER)
{
/* Construct the error DITL see IM I-427 */
errorDITL.itemCount = 1; /* total of two items */
/* OK button */
errorDITL.items[0].pointer = 0L;
errorDITL.items[0].boundsRect.top = 96;
errorDITL.items[0].boundsRect.left = 137;
errorDITL.items[0].boundsRect.bottom = 116;
errorDITL.items[0].boundsRect.right = 195;
errorDITL.items[0].itemType = (char) btnCtrl | ctrlItem;
errorDITL.items[0].itemData[0] = 2;
errorDITL.items[0].itemData[1] = 'O';
errorDITL.items[0].itemData[2] = 'K';
/* Static Text */
errorDITL.items[1].pointer = 0L;
errorDITL.items[1].boundsRect.top = 13;
errorDITL.items[1].boundsRect.left = 65;
errorDITL.items[1].boundsRect.bottom = 82;
errorDITL.items[1].boundsRect.right = 318;
errorDITL.items[1].itemType = (char) statText;
errorDITL.items[1].itemData[0] = 2;
errorDITL.items[1].itemData[1] = '^';
errorDITL.items[1].itemData[2] = '0';
/* Make the DITL into a resource */
thisHandle = NewHandle (sizeof (DialogList));
BlockMove (&errorDITL, *thisHandle, sizeof (DialogList));
AddResource (thisHandle, 'DITL', BASE_RES_ID, "\pError DITL");
thisErr = ResError ();
/* Construct the error ALRT IM-I 426 */
thisAlert.boundsRect.top = 40;
thisAlert.boundsRect.left = 40;
thisAlert.boundsRect.bottom = 172;
thisAlert.boundsRect.right = 368;
thisAlert.itemsID = BASE_RES_ID;
thisAlert.stages = 0x7775;
/* Make the ALRT into a resource */
thisHandle = NewHandle (sizeof (AlertTemplate));
BlockMove (&thisAlert, *thisHandle, sizeof (AlertTemplate));
AddResource (thisHandle, 'ALRT', BASE_RES_ID, "\pError ALRT");
thisErr = ResError ();
}
else
ReleaseResource (thisHandle);
}
main()
{
int i;
/* Maximize available memory */
MaxApplZone ();
/* Be sure to have plenty of master pointer blocks */
for (i = 0; i < NUM_MASTER_BLOCKS; i++)
MoreMasters();
Init ();
while (!gDone)
{
HandleEvent (&gTheEvent);
}
} /* main */